package com.shutong.edu.invoke.retry.model;

import com.shutong.edu.invoke.retry.annotation.EnableRetry;
import com.shutong.edu.invoke.retry.provider.FallbackProvider;
import net.sf.cglib.proxy.MethodProxy;
import org.aspectj.lang.ProceedingJoinPoint;

import java.lang.reflect.Method;
import java.util.Map;

/**
 *
 * @ClassName: RetryInfo.java
 * @author: Naughty Guo
 * @date: Jun 2, 2016
 */
public class RetryInfo {

    private Class<? extends Throwable>[] exceptions;
    private Class<? extends Throwable>[] ignoreExceptions;
    private Object[] arguments;
    private Object invokeResult;
    private Throwable invokeException;
    private Method targetMethod;
    private Object targetProxyObject;
    private Object targetOriginalObject;
    private short times;
    private long interval;
    private short executedTimes;
    private String fallbackMethod;
    private Class<? extends FallbackProvider> fallbackProvider;
    private String val;
    private String exp;
    private Map<Object, Object> expContext;
    private ProceedingJoinPoint proceedingJoinPoint;
    private MethodProxy methodProxy;
    private boolean enableRetry;

    public Class<? extends Throwable>[] getExceptions() {
        return exceptions;
    }

    public void setExceptions(Class<? extends Throwable>[] exceptions) {
        this.exceptions = exceptions;
    }

    public Class<? extends Throwable>[] getIgnoreExceptions() {
        return ignoreExceptions;
    }

    public void setIgnoreExceptions(Class<? extends Throwable>[] ignoreExceptions) {
        this.ignoreExceptions = ignoreExceptions;
    }

    public Object[] getArguments() {
        return arguments;
    }

    public void setArguments(Object[] arguments) {
        this.arguments = arguments;
    }

    public short getTimes() {
        return times;
    }

    public void setTimes(short times) {
        this.times = times;
    }

    public long getInterval() {
        return interval;
    }

    public void setInterval(long interval) {
        this.interval = interval;
    }

    public short getExecutedTimes() {
        return executedTimes;
    }

    public void setExecutedTimes(short executedTimes) {
        this.executedTimes = executedTimes;
    }

    public Object getInvokeResult() {
        return invokeResult;
    }

    public void setInvokeResult(Object invokeResult) {
        this.invokeResult = invokeResult;
    }

    public Throwable getInvokeException() {
        return invokeException;
    }

    public void setInvokeException(Throwable invokeException) {
        this.invokeException = invokeException;
    }

    public Method getTargetMethod() {
        return targetMethod;
    }

    public void setTargetMethod(Method targetMethod) {
        this.targetMethod = targetMethod;
    }

    public String getTargetMethodName() {
        if (null != targetMethod) {
            return targetMethod.getName();
        }
        return null;
    }

    public Object getTargetProxyObject() {
        return targetProxyObject;
    }

    public void setTargetProxyObject(Object targetProxyObject) {
        this.targetProxyObject = targetProxyObject;
    }

    public Object getTargetOriginalObject() {
        return targetOriginalObject;
    }

    public void setTargetOriginalObject(Object targetOriginalObject) {
        this.targetOriginalObject = targetOriginalObject;
    }

    public String getFallbackMethod() {
        return fallbackMethod;
    }

    public void setFallbackMethod(String fallbackMethod) {
        this.fallbackMethod = fallbackMethod;
    }

    public Class<? extends FallbackProvider> getFallbackProvider() {
        return fallbackProvider;
    }

    public void setFallbackProvider(Class<? extends FallbackProvider> fallbackProvider) {
        this.fallbackProvider = fallbackProvider;
    }

    public String getVal() {
        return val;
    }

    public void setVal(String val) {
        this.val = val;
    }

    public String getExp() {
        return exp;
    }

    public void setExp(String exp) {
        this.exp = exp;
    }

    public Map<Object, Object> getExpContext() {
        return expContext;
    }

    public void setExpContext(Map<Object, Object> expContext) {
        this.expContext = expContext;
    }

    public ProceedingJoinPoint getProceedingJoinPoint() {
        return proceedingJoinPoint;
    }

    public void setProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        this.proceedingJoinPoint = proceedingJoinPoint;
    }

    public MethodProxy getMethodProxy() {
        return methodProxy;
    }

    public void setMethodProxy(MethodProxy methodProxy) {
        this.methodProxy = methodProxy;
    }

    public boolean isEnableRetry() {
        return enableRetry;
    }

    public void setEnableRetry(boolean enableRetry) {
        this.enableRetry = enableRetry;
    }

    /**
     * increment executed times.
     *
     * @author Naughty Guo Jun 2, 2016
     */
    public void incrementExecutedTimes() {
        this.executedTimes++;
    }

    /**
     * check whether ignoreExceptions contains target exception.
     *
     * @param throwable
     * @return boolean
     * @author Naughty Guo Jun 2, 2016
     */
    public boolean isIgnoreException(Throwable throwable) {
        return containsException(0, throwable);
    }

    /**
     * check whether target exception in retry list.
     *
     * @param throwable
     * @return boolean
     * @author Naughty Guo Jun 2, 2016
     */
    public boolean isRetryException(Throwable throwable) {
        return containsException(1, throwable);
    }

    /**
     * fill other RetryInfo property value.
     *
     * @param enableRetry
     * @author Naughty Guo Jun 2, 2016
     */
    public void fill(EnableRetry enableRetry) {
        setExceptions(enableRetry.exceptions());
        setIgnoreExceptions(enableRetry.ignoreExceptions());
        setTimes(enableRetry.times());
        setInterval(enableRetry.interval());
        setFallbackMethod(enableRetry.fallbackMethod());
        setFallbackProvider(enableRetry.fallbackProvider());
        setVal(enableRetry.val());
        setExp(enableRetry.exp());
    }

    /**
     * create RetryInfo by EnableRetry
     *
     * @param enableRetry
     * @return RetryInfo
     * @author Naughty Guo Jun 2, 2016
     */
    public static RetryInfo create(EnableRetry enableRetry) {
        RetryInfo retryInfo = new RetryInfo();
        retryInfo.setExceptions(enableRetry.exceptions());
        retryInfo.setIgnoreExceptions(enableRetry.ignoreExceptions());
        retryInfo.setTimes(enableRetry.times());
        retryInfo.setInterval(enableRetry.interval());
        retryInfo.setFallbackMethod(enableRetry.fallbackMethod());
        retryInfo.setFallbackProvider(enableRetry.fallbackProvider());
        retryInfo.setVal(enableRetry.val());
        retryInfo.setExp(enableRetry.exp());
        return retryInfo;
    }

    /**
     * check whether contains target exception.
     *
     * @param checkType
     * @return boolean
     * @author Naughty Guo Jun 2, 2016
     */
    private boolean containsException(int checkType, Throwable throwable) {
        if (checkType == 0) {
            if (null != ignoreExceptions) {
                for (Class<?> ignoreException : ignoreExceptions) {
                    if (ignoreException == throwable.getCause().getClass()) {
                        return true;
                    }
                }
            }
        } else {
            for (Class<?> exception : exceptions) {
                if (exception == Throwable.class) {
                    return true;
                }
                if (exception == throwable.getCause().getClass()) {
                    return true;
                }
            }
        }
        return false;
    }
}
